#/usr/bin/python27
#-*- coding: utf-8 -*-
#  MS-SMS Enumerate class attribute checking program
#
#  Created by wuweihui@cn.ibm.com

import sys
import re
from CIMConnection.CIMlib import *
 
SMS_CLASS_LIST = []
def get_sms_class_list():
   global SMS_CLASS_LIST
   if not SMS_CLASS_LIST:
       with open('msclasslist.txt', 'rb') as fp:
           SMS_CLASS_LIST = fp.read().strip().splitlines()
   return SMS_CLASS_LIST

MS_STORAGE_CONFIGURATION = {}
def sms_probe(level=2):
   global MS_STORAGE_CONFIGURATION
   if int(level) not in [0, 1, 2]:
      return
   print "Reading configuration from storage,\nThis may take few minutes, Please wait......"
   MS_STORAGE_CONFIGURATION = {}
   if int(level) == 0: cl = ['MSFT_SMSystem']
   elif int(level) == 1: cl = ['MSFT_SMSystem', 'MSFT_SMPool']
   else: cl = get_sms_class_list()
   for c in cl:
      MS_STORAGE_CONFIGURATION[c] = ms_enumerate(c, filter='all')
   print "Read configuration finished!"
   
def split_ms_class_name(cn):
   """
   return [ClassID, Parent, Child]
   """
   classid = cn.replace('MSFT_SM', '')
   if classid.find('To') != -1:
      parent, child = classid.split("To")
      classid = ''
   else:
      parent = child = ''
   return classid, parent, child

def _build_ms_class_by_id(id):
   return "MSFT_SM" + id

def get_ms_class_associations():
   ms_class_association = {}
   for cn in get_sms_class_list():
      classid, parent, child = split_ms_class_name(cn)
      if not classid:
         if child == 'Volume':
            child = 'StorageVolume'
         if child == 'Setting':
            child = 'PoolSetting'
         if child == 'Capabilities':
            child = 'SystemCapabilities'
         if child == 'iSCSIPortal':
            child = 'iSCSIPortalEndpoint'
         ms_class_association[cn] = [_build_ms_class_by_id(parent), _build_ms_class_by_id(child)]

   return ms_class_association
   
def _parse_ref_attr(v):
   """
   return [ClassName, AttributeName, Value]
   
   Reference Child=MSFT_SMStorageHardwareID.ObjectId="root/ibm:h9.186.96.201:5989;IBMTSDS_StorageHardwareID.InstanceID=%'IBMTSDS:IBM.2810-1300207-100526%'"
   Reference Parent=MSFT_SMSystem.ObjectId="root/ibm:h9.186.96.201:5989;IBMTSDS_StorageSystem.CreationClassName=%'IBMTSDS_StorageSystem%',Name=%'IBM.2810-1300207%'"
   """
   re_assoc_attr = re.compile(r'(\w+)\.(\w+)\=\"*(.*?)\"*$')
   ret = re_assoc_attr.match(v)
   if ret:
      cn, attrname, value = ret.groups()
   else:
      cn = attrname = value = ''
   
   return cn, attrname, value
   
FIXED_MS_ASSOC = ["MSFT_SMPoolToSetting", "MSFT_SMPoolToVolume", "MSFT_SMDiscoveryToFabric", 
                  "MSFT_SMSystemToStorageGroup", "MSFT_SMSystemToPool", "MSFT_SMDiscoveryToSystem",
                  "MSFT_SMFCPortToProtocolEndpoint", "MSFT_SMSystemToFCPort", "MSFT_SMSystemToCapabilities",
                  "MSFT_SMZoneSetToZone", "MSFT_SMSystemToStorageHardwareID", "MSFT_SMSystemToEndpoint",
                  "MSFT_SMEndpointToiSCSIPortal", "MSFT_SMSwitchToFCPort"]
def check_ms_local_associations():   
   ms_class_association = get_ms_class_associations()
   for cn, [parent, child] in ms_class_association.items():
      if not MS_STORAGE_CONFIGURATION[cn]:
         if MS_STORAGE_CONFIGURATION[parent] and MS_STORAGE_CONFIGURATION[child]:
            print "*Error* %s association can not be found!" % cn
         else:
            print "*Warning* %s association not found!" % cn
      
      else:
         if not MS_STORAGE_CONFIGURATION[parent]:
            print "*Error* %s parent %s association can not be found!" % (cn, parent)
         elif not MS_STORAGE_CONFIGURATION[child]:
            print "*Error* %s child %s association can not be found!" % (cn, child)
         else:
            cn_in_FIXED_MS_ASSOC = cn in FIXED_MS_ASSOC
            if cn_in_FIXED_MS_ASSOC:
               childlist = MS_STORAGE_CONFIGURATION[child].Copy()
            for assoc in MS_STORAGE_CONFIGURATION[cn]:
               pcn, pattr, pvalue = _parse_ref_attr(assoc.Parent)
               ccn, cattr, cvalue = _parse_ref_attr(assoc.Child)
               if pcn != parent:
                  print "*Warning* parent association not same as definition %s and %s" % (pcn, parent)
               if ccn != child:
                  print "*Warning* child association not same as definition %s and %s" % (ccn, child)
               if not MS_STORAGE_CONFIGURATION[pcn].GetItemList(pattr, pvalue):
                  print "*Error* %s parent %s association can not be found!" % (cn, parent)
               if not MS_STORAGE_CONFIGURATION[ccn].GetItemList(cattr, cvalue):
                  print "*Error* %s child %s association can not be found!" % (cn, child)
               elif cn_in_FIXED_MS_ASSOC:
                  childlist.RemoveByKey(cattr, cvalue, ccn)
            if cn_in_FIXED_MS_ASSOC and childlist:
               print "*Error* association inconsistent at %s" % cn
               for inst in childlist:
                  print "        %s" % inst.__getkeyvalue__()

def check_ms_null_values():
   for cn, instlist in MS_STORAGE_CONFIGURATION.items():
      for inst in instlist:
         attrs = inst.__getattrnames__()
         for attrname in attrs:
            if inst.__getattribute__(attrname) == "NULL":
               print "*Warning* %s.%s is NULL at %s" % (cn, attrname, inst.__getkeyvalue__())
      
def check_empty_instance():
   for cn, instlist in MS_STORAGE_CONFIGURATION.items():
      if not instlist:
         print "*Warning* instanse of %s not found!" % cn
      
def redirect_output(fp):
   if isinstance(fp, basestring):
      fp = open(fp, 'wb')
   oldout = sys.stdout
   sys.stdout = fp
   return oldout


def get_ibm_class_list():
    with open('ibmclasslist.txt', 'rb') as fp:
        return fp.read().strip().splitlines()

def cim_probe():
   global CIM_STORAGE_CONFIGURATION
   CIM_STORAGE_CONFIGURATION = {}
   print "Reading configuration from storage,\nThis may take few minutes, Please wait......"
   for cn in get_ibm_class_list():
      CIM_STORAGE_CONFIGURATION[cn] = cim_enumerate(cn)
   print "Read configuration finished!"
      
def test_main():
   sms_probe(2)
   print "\n=========== Checking Null Values: ===========\n"
   check_ms_null_values()
   print "\n=========== Value Check Finished! ===========\n"
   print "\n=========== Checking Empty Instances: ===========\n"
   check_empty_instance()
   print "\n=========== Empty Instance Check Finished! ===========\n"
   print "\n=========== Checking Associations ===========\n"
   check_ms_local_associations()
   print "\n=========== Association Check Finished! ===========\n"
   
   

def _test_print_assoc_class():
   for k, v in get_ms_class_associations().items():
      print k


if __name__ == '__main__':
   # _test_print_assoc_class()
   redirect_output("_testlog.log")
   test_main()